#include "listview.hpp"

void TreeItem::UpdatePosition(long long n){
    //上多
    extend_start+=n;
}

ImageItem::ImageItem(SDL_Texture* t):_t(t){
    SDL_QueryTexture(t,0,0,&w,&h);
}
void ImageItem::Draw(Renderer* render,ListState state,SDL_Rect& rect){
    render->DrawTexture(0,&rect,_t);
}
ImageItem::~ImageItem(){
    SDL_DestroyTexture(_t);
}
const char* TextItem::text(){return _text;}
TextItem::TextItem(Window* window,const char* text,Font* font,SDL_Color normal_color,
SDL_Color on_color,SDL_Color select_color){
    _text=text;
    this->font=font;
    this->on_color=on_color;
    this->select_color=select_color;
    this->normal_color=normal_color;
    auto r=window->renderer;
    _normal=r->RenderText(font,text,normal_color);
    _select=r->RenderText(font,text,select_color);
    _on=r->RenderText(font,text,on_color);
    SDL_QueryTexture(_normal,NULL,NULL,&w,&h);
}
void TextItem::Draw(Renderer* render,ListState state,SDL_Rect& r){
    SDL_Rect src{0,0,min(w,r.w),min(h,r.h)};
    switch(state){
        case ListState::Normal:
        render->DrawTexture(&src,&r,_normal);
        return;
        case ListState::Select:
        render->DrawTexture(&src,&r,_select);
        return;
        case ListState::On:
        render->DrawTexture(&src,&r,_on);
        return;
    }
}
//将更新三个不同状态的文本缓存
void TextItem::SetText(Window* window,const char* text,Font* font,SDL_Color normal_color,
SDL_Color on_color,SDL_Color select_color){
    _text=text;
    this->on_color=on_color;
    this->font=font;
    this->normal_color=normal_color;
    this->select_color=select_color;
    auto r=window->renderer;
    _normal=r->RenderText(font,text,normal_color);
    _select=r->RenderText(font,text,select_color);
    _on=r->RenderText(font,text,on_color);
    SDL_QueryTexture(_normal,NULL,NULL,&w,&h);
}
TextItem::~TextItem(){
    SDL_DestroyTexture(_normal);
    SDL_DestroyTexture(_select);
    SDL_DestroyTexture(_on);
}

void ListView::Clear(bool freed){
    if(freed){
        for(auto item=items.begin(),end=items.end();item!=end;item++){
            delete *item;
        }
    }
    items.clear();
    select=-1;
    on=-1;
    cur_item=0;
}

void ListView::ListenScroll(void* list){
    if(((ListView*)list)->pressed){
        auto size=((ListView*)list)->items.size();
        if(size){
            int x,y;
            auto &r=((ListView*)list)->_area;
            SDL_GetMouseState(&x,&y);
            if(y<r.y){
                ((ListView*)list)->cur_item=0;
                ((ListView*)list)->scroll.y=r.y;
                return;
            }
            ((ListView*)list)->cur_item=min(size-1.,(y-r.y)/((ListView*)list)->_factor);
            ((ListView*)list)->scroll.y=r.y+((ListView*)list)->_factor*((ListView*)list)->cur_item;
        }
        return;
    }
    ((ListView*)list)->_belong->RemoveTask(((ListView*)list)->_task);
    ((ListView*)list)->_task=NULL;
}

void ListView::ListenMouse(void* list){
    auto &r=((ListView*)list)->_area;
    int x,y;
    SDL_GetMouseState(&x,&y);
    if(x<r.x||x>r.x+r.w||y<r.y||y>r.y+r.h){
        ((ListView*)list)->on=-1;
        ((ListView*)list)->_in=false;
        ((ListView*)list)->_belong->RemoveTask(((ListView*)list)->_task);
        ((ListView*)list)->scroll_color=((ListView*)list)->normal_scroll_color;
        ((ListView*)list)->_task=NULL;
        SDL_SetCursor(arrow_cursor);
        return;
    }
    auto &g=((ListView*)list)->scroll;
    if(x<g.x){
        //项
        ((ListView*)list)->scroll_color=((ListView*)list)->normal_scroll_color;
        ((ListView*)list)->on=((ListView*)list)->cur_item+
        (y-r.y-((ListView*)list)->space)/(((ListView*)list)->item_height+((ListView*)list)->space);
        if(((ListView*)list)->on>=((ListView*)list)->items.size()){
            ((ListView*)list)->on=-1;
            SDL_SetCursor(arrow_cursor);
            return;
        }
        ((ListView*)list)->items.operator[](((ListView*)list)->on)->_on((ListView*)list,x,y);
        SDL_SetCursor(button_cursor);
        return;
    }
    ((ListView*)list)->on=-1;
    if(g.x<=x&&x<=g.x+r.w&&g.y<=y&&y<=g.y+g.h){
        ((ListView*)list)->scroll_color=((ListView*)list)->on_scroll_color;
        return;
    }
    ((ListView*)list)->scroll_color=((ListView*)list)->normal_scroll_color;
}
size_t ListView::selected(){return select;}

ListView::ListView(Window* window,SDL_Rect& rect,int space,int item_h,
SDL_Color background,SDL_Color select_color,SDL_Color on_color,
SDL_Color normal_scroll_color,SDL_Color on_scroll_color):BaseListView(window){
    _in=false;
    scroll.w=10,scroll.x=rect.x+rect.w-10,scroll.y=rect.y,scroll.h=rect.h;
    cur_item=0;
    this->select_color=select_color;
    this->on_color=on_color;
    this->background=background;
    this->space=space;
    this->normal_scroll_color=normal_scroll_color;
    this->on_scroll_color=on_scroll_color;
    this->scroll_color=normal_scroll_color;
    item_height=item_h;
    _area=rect;
    select=-1;
    on=-1;
    max_item=(rect.h)/(item_h+space);
    _t=window->renderer->CreateTexture(rect.w,item_h);
    SDL_SetTextureBlendMode(_t,SDL_BLENDMODE_BLEND);
    _task=NULL;
}
ListView::ListView(Window* window):BaseListView(window){
    _task=NULL;
    _in=false;
    background=Color::Gray;
    item_height=14;
    space=3;
    select_color=Color::Blue;
    on_color=Color::SkyBlueGray;
    normal_scroll_color={0,0,0,120};
    on_scroll_color={0,0,0,220};
    cur_item=0;
    _t=window->renderer->CreateTexture(0,0);
    select=-1;
    on=-1;
    max_item=0;
}

void ListView::ItemChanged(){
    _factor=(double)_area.h/(double)items.size();
    scroll.h=ceil(_factor);
}
void ListView::SetSize(int w,int h){
    SDL_DestroyTexture(_t);
    _t=_belong->renderer->CreateTexture(w-5,h);
    _area.w=w,_area.h=h;
}
void ListView::SetPosition(int x,int y){
    scroll.x+=x-_area.x,scroll.y+=y-_area.y;
    _area.x=x,_area.y=y;
}
void ListView::OffsetPosition(int x,int y){
    scroll.x+=x,scroll.y+=y;
    _area.x+=x,_area.y+=y;
}

ListItem* ListView::AddItem(ListItem* item){
    item->OnAdd(&items);
    _factor=(double)_area.h/(double)items.size();
    scroll.h=ceil(_factor);
    return item;
}
void ListView::AddItem(vector<ListItem*>* src){
    auto& i=items;
    i.reserve(items.size()+src->size());
    for(auto item=src->begin(),end=src->end();item!=end;item++){
        (*item)->OnAdd(&i);
    }
    _factor=(double)_area.h/(double)items.size();
    scroll.h=ceil(_factor);
}
ListItem* ListView::RemoveItem(size_t index){
    if(index<items.size()){
        cur_item=0;
        on=-1;
        select=-1;
        auto i=items.operator[](index);
        for(auto item=items.erase(items.begin()+index),end=items.end();item!=end;item++)(*item)->UpdatePosition(-1);
        if(items.empty()){
            scroll.h=_area.h;
            return i;
        }
        _factor=(double)_area.h/(double)items.size();
        scroll.h=ceil(_factor);
        return i;
    }
    throw out_of_range(to_string(index));
}

void ListView::_mouse_wheel(SDL_MouseWheelEvent& event){
    long long s=(long long)items.size();
    if(!s){
        return;
    }
    auto y=event.y;
    if(y<0){
        //下
        if(cur_item-y<s){
            cur_item-=y;
            scroll.y-=ceil(y*_factor);
            return;
        }
        cur_item=s-1;
        scroll.y=_area.y+_area.h-ceil(_factor);
        return;
    }
    if(y>0){
        //上
        if(cur_item-y<=0){
            cur_item=0;
            scroll.y=_area.y;
            return;
        }
        cur_item-=y;
        scroll.y-=ceil(y*_factor);
    }
}
void ListView::_press(int x,int y,int clicks,unsigned char key){
    if(!_enabled){
        return;
    }
    if(x-_area.x>_area.w-10){
        auto w=_belong;
        if(_task){
            w->RemoveTask(_task);
        }
        _task=w->AddTask(ListenScroll,this,1,false,true);
        return;
    }
}
void ListView::_release(int x,int y,int clicks,unsigned char key){
    if(!_enabled){
        return;
    }
    auto t=y-_area.y;
    if(t<space||t>_area.h-space){
        return;
    }
    if(x-_area.x>_area.w-10){
        //滚动条
        return;
    }
    if(on==-1){
        return;
    }
    select=on;
    if(clicks==1){
        items.operator[](select)->_click(this,x,y,key);
        return;
    }
    items.operator[](select)->_double_click(this,x,y,key);
}
void ListView::_mouse_move(int x,int y){
    if(!_task&&_enabled){
        _in=true;
        _task=_belong->AddTask(ListenMouse,this,1,false,true);
    }
}
void ListView::_draw(){
    auto r=_belong->renderer;
    r->SetColor(background);
    r->FillRect(_area);
    auto h=item_height;
    SDL_Rect rect{_area.x,_area.y+space,_area.w,item_height},_r{0,0,_area.w,item_height};
    auto t=_t;
    for(auto item=items.begin()+cur_item,end=items.end(),stop=item+max_item,
    selected=items.begin()+select,on_item=items.begin()+on;item!=end;item++){
        r->SetTarget(t);
        ListState state;
        if(item==selected){
            state=ListState::Select;
            r->SetColor(select_color);
        }
        else if(item==on_item){
            state=ListState::On;
            r->SetColor(on_color);
        }
        else{
            state=ListState::Normal;
            r->SetColor(background);
        }
        r->Clear();
        (*item)->Draw(r,state,_r);
        r->SetTarget(NULL);
        if(item==stop){
            rect.h=_area.h-rect.y;
            SDL_Rect src{0,0,rect.w,rect.h};
            r->DrawTexture(&src,&rect,t);
            break;
        }
        else{
            r->DrawTexture(0,&rect,t);
        }
        rect.y+=space+h;
    }
    if(!_in){
        return;
    }
    //绘制滚动条
    r->SetColor(scroll_color);
    r->FillRect(scroll);
}
void ListView::_key_down(SDL_Keysym& sym){}
ListView::~ListView(){
    if(_task){
        _belong->RemoveTask(_task);
    }
}

void TreeItem::CollectCount(size_t& count){
    for(auto item=children->begin(),end=children->end();item!=end;item++){
        auto t=(TreeItem*)*item;
        if(t->extend){
            t->CollectCount(count);
            count+=t->children->size();
        }
    }
    extend=false;
}
void TreeItem::ExtendOrFold(){
    if(extend){
        //折叠
        auto begin=list->begin()+extend_start+1;
        auto size=children->size();
        CollectCount(size);
        auto s=-size;
        for(auto item=list->erase(begin,begin+size),end=list->end();item!=end;item++)(*item)->UpdatePosition(s);
        return;
    }
    //extend
    auto begin=list->begin();
    size_t adder=extend_start+1;
    auto size=children->size();
    for(auto item=begin+adder;item!=list->end();item++)(*item)->UpdatePosition(size);
    list->insert(begin+adder,children->begin(),children->end());//因为存在多个不同列表项 需要做变更缓存
    for(auto item=children->begin(),end=children->end();item!=end;item++){
        ((TreeItem*)*item)->extend_start=adder++;
        ((TreeItem*)*item)->extend=false;
    }
    extend=true;
}
//使用时必须统一为TreeItem
TreeItem::TreeItem(Window* window,const char* text,int extend_offset,Font* font,SDL_Color normal_color,
SDL_Color on_color,SDL_Color select_color):
TextItem(window,text,font,normal_color,on_color,select_color){
    children=new vector<TreeItem*>();
    offset=0;
    _offset=extend_offset;
    depth=0;
    parent=NULL;
    extend=false;
}
//此方法中进行插入操作
void TreeItem::OnAdd(vector<ListItem*>* l){
    extend=false;
    list=l;
    extend_start=l->size();
    l->push_back(this);
}
TreeItem* TreeItem::Add(TreeItem* item){
    item->list=list,item->extend=false;
    if(extend){
        item->extend_start=extend_start+1;
        list->insert(list->begin()+extend_start,this);
    }
    children->push_back(item);
    item->offset=offset+item->_offset;
    item->depth=depth+1;
    item->parent=this;
    return item;
}
TreeItem::~TreeItem(){
    if(!extend){
        for(auto item=children->begin(),end=children->end();item!=end;item++){
            delete *item;
        }
    }
    delete children;
}
void TreeItem::_click(BaseListView* list,int x,int y,unsigned char key){
    ExtendOrFold();
    list->ItemChanged();
    OnClick(x,y,key);
}
void TreeItem::_double_click(BaseListView* list,int x,int y,unsigned char key){
    ExtendOrFold();
    list->ItemChanged();
    OnDoubleClick(x,y,key);
}
void TreeItem::Draw(Renderer* render,ListState state,SDL_Rect& rect){
    if(!children->empty()){
        SDL_Point p1{offset+3,3};
        if(extend){
            SDL_Point p2{5+offset,3+(h>>1)};
            render->SetColor(normal_color);
            render->DrawLine(p1,p2);
            p1.x=10+offset;
            render->DrawLine(p2,p1);
        }
        else{
            SDL_Point p2{5+offset,h>>1};
            render->SetColor(normal_color);
            render->DrawLine(p1,p2);
            p1.y=h-3;
            render->DrawLine(p2,p1);
        }
    }
    SDL_Rect r{10+offset+rect.x,0,w,h};
    switch(state){
        case ListState::Normal:
        render->DrawTexture(NULL,&r,_normal);
        return;
        case ListState::Select:
        render->DrawTexture(NULL,&r,_select);
        return;
        case ListState::On:
        render->DrawTexture(NULL,&r,_on);
        return;
    }
}

void TreeTextItem::OnClick(int x,int y,unsigned char key){}
void TreeTextItem::OnDoubleClick(int x,int y,unsigned char key){}

TreeTextItem::TreeTextItem(Window* window,const char* text,int extend_offset,
Font* font,SDL_Color normal_color,SDL_Color on_color,SDL_Color select_color)
:TreeItem(window,text,extend_offset,font,normal_color,on_color,select_color){}
